package com.yeskery.nut.util;

import com.yeskery.nut.bind.FitValueHelper;
import com.yeskery.nut.core.Environment;
import com.yeskery.nut.core.KeyAndValue;
import com.yeskery.nut.core.NutException;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 表达式工具类
 * @author YESKERY
 * 2024/6/5
 */
public class ExpressionUtils {

    /** 开始符号 */
    private static final String START_SYMBOL = "${";

    /** 结束符号 */
    private static final String END_SYMBOL = "}";

    /** 分隔符 */
    private static final String PIPELINE_SYMBOL = ":";

    /** 分组分隔符 */
    private static final String GROUP_SEPARATOR = "-";

    /** 分组开始符 */
    private static final String GROUP_SEPARATOR_START = "[";

    /** 分组结束符 */
    private static final String GROUP_SEPARATOR_END = "]";

    /**
     * 私有化构造方法
     */
    private ExpressionUtils() {
    }

    /**
     * 获取表达式的值
     * @param environment 环境对象
     * @param expression 表达式
     * @param ifNotExistExceptionSupplier 如无法匹配值时抛出的异常
     * @return 表达式的值
     */
    public static String getExpressValue(Environment environment, String expression, Supplier<? extends NutException> ifNotExistExceptionSupplier) {
        expression = expression.trim();
        if (isExpression(expression)) {
            ExpressionValue expressionValue = getExpressionValue(expression);
            String value = environment.getEnvProperties().getProperty(expressionValue.name);
            if (value == null) {
                String modifyName = environment.getEnvProperties().entrySet().stream()
                        .filter(e -> StringUtils.getModifyName(e.getKey().toString()).equals(expressionValue.name))
                        .findFirst()
                        .map(e -> e.getKey().toString())
                        .orElse(null);
                if (modifyName != null) {
                    return getExpressValue(environment, START_SYMBOL + modifyName + END_SYMBOL, ifNotExistExceptionSupplier);
                } else {
                    if (expressionValue.defaultValue == null && ifNotExistExceptionSupplier != null) {
                        throw ifNotExistExceptionSupplier.get();
                    }
                }
            }
            return value != null ? value : expressionValue.defaultValue;
        }
        return expression;
    }

    /**
     * 获取表达式的数组值
     * @param environment 环境对象
     * @param expression 表达式
     * @param field 字段
     * @param expressInvalidExceptionSupplier 表达式无效时抛出的异常
     * @return 表达式的值
     */
    public static Object getExpressArray(Environment environment, String expression, Field field,
                                         Supplier<? extends NutException> expressInvalidExceptionSupplier) {
        expression = expression.trim();
        if (!isExpression(expression)) {
            if (expressInvalidExceptionSupplier != null) {
                throw expressInvalidExceptionSupplier.get();
            }
        } else {
            FitValueHelper fitValueHelper = FitValueHelper.getInstance();
            Class<?> clazz = field.getType().getComponentType();
            if (!fitValueHelper.isBasicTypeClass(clazz)) {
                throw new NutException("Express Only Support Base Type.");
            }
            ExpressionValue expressionValue = getExpressionValue(expression);
            String value = environment.getEnvProperty(expressionValue.name);
            if (!StringUtils.isEmpty(value)) {
                String[] splits = value.split(",");
                Object array = Array.newInstance(clazz, splits.length);
                for (int i = 0; i < splits.length; i++) {
                    Array.set(array, i, fitValueHelper.getFitParamValue(field.getName(), splits[i].trim(), clazz).getData());
                }
                return array;
            }

            List<String> values = getExpressionListFormEnvironment(environment, expressionValue.name);
            if (!values.isEmpty()) {
                Object array = Array.newInstance(clazz, values.size());
                for (int i = 0; i < values.size(); i++) {
                    Array.set(array, i, fitValueHelper.getFitParamValue(field.getName(), values.get(i).trim(), clazz).getData());
                }
                return array;
            }

            if (!StringUtils.isEmpty(expressionValue.defaultValue)) {
                String[] splits = expressionValue.defaultValue.split(",");
                Object array = Array.newInstance(clazz, splits.length);
                for (int i = 0; i < splits.length; i++) {
                    Array.set(array, i, fitValueHelper.getFitParamValue(field.getName(), splits[i].trim(), clazz).getData());
                }
                return array;
            }
        }
        String modifyName;
        if (expression.contains(GROUP_SEPARATOR) && !StringUtils.isEmpty(modifyName = StringUtils.getModifyName(expression)) && !modifyName.equals(expression)) {
            return getExpressArray(environment, modifyName, field, expressInvalidExceptionSupplier);
        } else {
            return Array.newInstance(field.getType().getComponentType(), 0);
        }
    }

    /**
     * 获取表达式的集合值
     * @param environment 环境对象
     * @param expression 表达式
     * @param field 字段
     * @param expressInvalidExceptionSupplier 表达式无效时抛出的异常
     * @return 表达式的值
     */
    public static Collection<Object> getExpressCollection(Environment environment, String expression, Field field,
                                                          Supplier<? extends NutException> expressInvalidExceptionSupplier) {
        expression = expression.trim();
        if (!isExpression(expression)) {
            if (expressInvalidExceptionSupplier != null) {
                throw expressInvalidExceptionSupplier.get();
            }
        } else {
            FitValueHelper fitValueHelper = FitValueHelper.getInstance();
            Class<?> clazz = ReflectUtils.getActualTypeArgumentType(field.getGenericType(), 0);
            if (!fitValueHelper.isBasicTypeClass(clazz)) {
                throw new NutException("Express Only Support Base Type.");
            }
            ExpressionValue expressionValue = getExpressionValue(expression);
            String value = environment.getEnvProperty(expressionValue.name);
            if (!StringUtils.isEmpty(value)) {
                String[] splits = value.split(",");
                Collection<Object> collection = ReflectUtils.createFieldTypeCollection(field, splits.length);
                for (String split : splits) {
                    collection.add(fitValueHelper.getFitParamValue(field.getName(), split.trim(), clazz).getData());
                }
                return collection;
            }
            List<String> values = getExpressionListFormEnvironment(environment, expressionValue.name);
            if (!values.isEmpty()) {
                Collection<Object> collection = ReflectUtils.createFieldTypeCollection(field, values.size());
                for (String str : values) {
                    collection.add(fitValueHelper.getFitParamValue(field.getName(), str.trim(), clazz).getData());
                }
                return collection;
            }
            if (!StringUtils.isEmpty(expressionValue.defaultValue)) {
                String[] splits = expressionValue.defaultValue.split(",");
                Collection<Object> collection = ReflectUtils.createFieldTypeCollection(field, splits.length);
                for (String split : splits) {
                    collection.add(fitValueHelper.getFitParamValue(field.getName(), split.trim(), clazz).getData());
                }
                return collection;
            }
        }
        String modifyName;
        if (expression.contains(GROUP_SEPARATOR) && !StringUtils.isEmpty(modifyName = StringUtils.getModifyName(expression)) && !modifyName.equals(expression)) {
            return getExpressCollection(environment, modifyName, field, expressInvalidExceptionSupplier);
        } else {
            return Collections.emptyList();
        }
    }

    /**
     * 获取表达式的集合值
     * @param environment 环境对象
     * @param expression 表达式
     * @param field 字段
     * @param expressInvalidExceptionSupplier 表达式无效时抛出的异常
     * @return 表达式的值
     */
    public static Map<String, Object> getExpressMap(Environment environment, String expression, Field field,
                                                    Supplier<? extends NutException> expressInvalidExceptionSupplier) {
        expression = expression.trim();
        if (!isExpression(expression)) {
            if (expressInvalidExceptionSupplier != null) {
                throw expressInvalidExceptionSupplier.get();
            }
        } else {
            FitValueHelper fitValueHelper = FitValueHelper.getInstance();
            Class<?> keyClazz = ReflectUtils.getActualTypeArgumentType(field.getGenericType(), 0);
            if (!String.class.equals(keyClazz)) {
                throw new NutException("Express Map Key Only Support String Type.");
            }
            Class<?> valueClazz = ReflectUtils.getActualTypeArgumentType(field.getGenericType(), 1);
            if (!fitValueHelper.isBasicTypeClass(valueClazz)) {
                throw new NutException("Express Map Key Only Support Base Type.");
            }
            ExpressionValue expressionValue = getExpressionValue(expression);
            String value = environment.getEnvProperty(expressionValue.name);
            if (!StringUtils.isEmpty(value)) {
                String[] splits = value.split(",");
                Map<String, Object> map = new HashMap<>(splits.length);
                for (String split : splits) {
                    String[] keyValue = split.trim().split("=");
                    if (keyValue.length != 2) {
                        throw new NutException("Express Map Key Value Format Invalid.");
                    }
                    map.put(keyValue[0], fitValueHelper.getFitParamValue(field.getName(), keyValue[1], valueClazz).getData());
                }
                return map;
            }
            List<KeyAndValue<String, String>> values = environment.getEnvProperties().entrySet().stream()
                    .filter(e -> e.getKey().toString().startsWith(expressionValue.name))
                    .map(e -> {
                        int startIndex = e.getKey().toString().indexOf(GROUP_SEPARATOR_START);
                        int endIndex = e.getKey().toString().lastIndexOf(GROUP_SEPARATOR_END);
                        if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
                            String key = e.getKey().toString().substring(startIndex + 1, endIndex);
                            return new KeyAndValue<>(key, e.getValue().toString());
                        }
                        return null;
                    })
                    .filter(Objects::nonNull)
                    .sorted(Comparator.comparing(KeyAndValue::getKey))
                    .collect(Collectors.toList());
            if (!values.isEmpty()) {
                Map<String, Object> map = new HashMap<>(values.size());
                for (KeyAndValue<String, String> value1 : values) {
                    map.put(value1.getKey(), fitValueHelper.getFitParamValue(field.getName(), value1.getValue(), valueClazz).getData());
                }
                return map;
            }
            if (!StringUtils.isEmpty(expressionValue.defaultValue)) {
                String[] splits = expressionValue.defaultValue.split(",");
                Map<String, Object> map = new HashMap<>(splits.length);
                for (String split : splits) {
                    String[] keyValue = split.trim().split("=");
                    if (keyValue.length != 2) {
                        throw new NutException("Express Map Key Value Format Invalid.");
                    }
                    map.put(keyValue[0], fitValueHelper.getFitParamValue(field.getName(), keyValue[1], valueClazz).getData());
                }
                return map;
            }
        }
        String modifyName;
        if (expression.contains(GROUP_SEPARATOR) && !StringUtils.isEmpty(modifyName = StringUtils.getModifyName(expression)) && !modifyName.equals(expression)) {
            return getExpressMap(environment, modifyName, field, expressInvalidExceptionSupplier);
        } else {
            return Collections.emptyMap();
        }
    }

    /**
     * 判断是否是表达式
     * @param expression 表达式
     * @return 是否是表达式
     */
    public static boolean isExpression(String expression) {
        return expression.startsWith(START_SYMBOL) && expression.endsWith(END_SYMBOL) && expression.length() > 3;
    }

    /**
     * 获取表达式值
     * @param expression 表达式
     * @return 表达式值
     */
    private static ExpressionValue getExpressionValue(String expression) {
        String name = expression.substring(START_SYMBOL.length(), expression.length() - END_SYMBOL.length());
        String defaultValue = null;
        if (name.contains(PIPELINE_SYMBOL)) {
            String originalName = name;
            int index = name.indexOf(PIPELINE_SYMBOL);
            name = originalName.substring(0, index);
            defaultValue = originalName.substring(index + 1);
        }
        ExpressionValue expressionValue = new ExpressionValue();
        expressionValue.name = name;
        expressionValue.defaultValue = defaultValue;
        return expressionValue;
    }

    /**
     * 从环境对象中获取表达式列表
     * @param environment 环境对象
     * @param expression 表达式
     * @param function 转换函数
     * @return 表达式列表
     */
    private static List<String> getExpressionListFormEnvironment(Environment environment, String expression, 
                                                                 Function<Map.Entry<Object, Object>, KeyAndValue<String, String>> function) {
        return environment.getEnvProperties().entrySet().stream()
                .filter(e -> e.getKey().toString().startsWith(expression))
                .map(function)
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(KeyAndValue::getKey))
                .map(KeyAndValue::getValue)
                .collect(Collectors.toList());
    }

    /**
     * 从环境对象中获取表达式列表
     * @param environment 环境对象
     * @param expression 表达式
     * @return 表达式列表
     */
    private static List<String> getExpressionListFormEnvironment(Environment environment, String expression) {
        return getExpressionListFormEnvironment(environment, expression, e -> {
            int startIndex = e.getKey().toString().indexOf(GROUP_SEPARATOR_START);
            int endIndex = e.getKey().toString().lastIndexOf(GROUP_SEPARATOR_END);
            if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
                String index = e.getKey().toString().substring(startIndex + 1, endIndex);
                try {
                    Integer.parseInt(index);
                    return new KeyAndValue<>(index, e.getValue().toString());
                } catch (NumberFormatException e1) {
                    return null;
                }
            }
            return null;
        });
    }

    /**
     * 表达式值对象
     */
    private static class ExpressionValue {
        /** 表达式名称 */
        private String name;
        /** 默认值 */
        private String defaultValue;
    }
}
